//结合清北学堂的DFS解法，改造更可读， 容易理解
#include <bits/stdc++.h>
using namespace std;

// 常量定义
const int MAXN = 2e5 + 5; // 最大节点数
const int MAXK = 10 + 5;  // 最大步数限制

// 全局变量
vector<int> G[MAXN];  // 图的邻接表表示
int n, m, k;          // n:节点数, m:边数, k:步数限制
char S[MAXN];         // 每个节点的字符标记（'A'或'B'）
int f[MAXN][MAXK][2]; // 记忆化数组：f[节点][ 步数][当前玩家]

/**
 * 深度优先搜索函数，用于判断在特定状态下当前玩家是否能获胜
 * @param v 当前节点
 * @param step 当前步数 ，一共2*k步
 * @param o 当前玩家（0:Alice, 1:Bob）
 * @return 当前玩家是否能获胜（1:表示 获胜, 0: 输）
 */

int dfs(int v, int step, string name) {
  // 基本情况：步数用完
  if (step == 2 * k + 1) {
    return S[v] == 'A';
  }

  // 记忆化检查
  int &res = f[v][step][name != "Alice"];
  if (res != -1)
    return res;

  // 统一逻辑：当前玩家能赢的条件是存在一个移动让对手输
  //  无论是Alice还是Bob，都希望存在一个移动能让对手输，每一轮逻辑相同，只是角色不同
  res = 0;

  for (auto son : G[v]) {
    // 计算对手在下一步的状态
    int opponent_result;
    if (name == "Alice") { // Alice回合 → Bob下一步
      opponent_result = dfs(son, step + 1, "Bob");
    } else { // Bob回合 → Al,ice下一步（步数加+1）
      opponent_result = dfs(son, step + 1, "Alice");
    }

    // 如果对手会输，当前玩家就能赢
    if (!opponent_result) {
      res = 1;
      break;
    }
  }

  return res;
}

/**
 * 解决单个测试用例的函数
 */
inline void Solve() {
  // 输入数据
  scanf("%d%d%d", &n, &m, &k); // 节点数、边数、步数限制
  scanf("%s", S + 1);          // 节点标记（从索引1开始）

  // 初始化图的邻接表
  for (int i = 1; i <= n; ++i)
    G[i].clear();

  // 输入边信息
  for (int i = 1; i <= m; ++i) {
    int u, v;
    scanf("%d%d", &u, &v);
    G[u].push_back(v); // 添加有向边
  }

  // 注释：使用memset会超时，所以手动初始化记忆化数组
  // memset(f,-1,sizeof(f)); TLE

  // 手动初始化记忆化数组为-1（未计算状态）
  for (int i = 0; i <= n; ++i) {
    for (int j = 0; j <= 2 * k + 1; ++j) {
      f[i][j][0] = f[i][j][1] = -1;
    }
  }

  // 从节点1开始，第一步开始，Alice先手，判断获胜者
  puts(dfs(1, 1, "Alice") ? "Alice" : "Bob");
}

/**
 * 主函数：处理多个测试用例
 */
int main() {
  int T;
  scanf("%d", &T);
  while (T--)
    Solve();
  return 0;
}